#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread/pthread.h>
#include <mach/host_priv.h>
#include <stdbool.h>
// 从HTTP请求头中解析Host字段
char* parse_host_header(const char* request_header) {
    const char* host_start = "Host: ";
    char* host_line = NULL;
    char* host_value = NULL;
    size_t host_line_len;

    // 查找Host行
    host_line = strstr(request_header, host_start);
    if (host_line) {
        // 计算Host值的长度
        host_line_len = strcspn(host_line + strlen(host_start), "\r\n");
        // 复制Host值
        host_value = strndup(host_line + strlen(host_start), host_line_len);
    }
    printf("host value: %s\n",host_value);
    return host_value;
}

// 执行域名解析并打印IP地址和端口
void resolve_and_print_ip(const char* host,char* hostip) {
    struct addrinfo hints;
    struct addrinfo *result, *rp;
    int s;
    char service[10];
    // 初始化hints结构体
    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_UNSPEC;    // 允许IPv4或IPv6
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;    // 填充主机地址

    // 获取地址信息
    s = getaddrinfo(host, service, &hints, &result);
    if (s != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
        return;
    }

    // 遍历结果并 IP地址 
    for (rp = result; rp != NULL; rp = rp->ai_next) {
        void* addr;
        char *ipver;
        if (rp->ai_family == AF_INET) { // IPv4
            struct sockaddr_in *ipv4 = (struct sockaddr_in *)rp->ai_addr;
            addr = &(ipv4->sin_addr);
            ipver = "IPv4";
            {
                char ipstr[INET6_ADDRSTRLEN] = {0};
                inet_ntop(rp->ai_family, addr, ipstr, sizeof(ipstr));
                strcpy(hostip, ipstr);
            }
            break;
        } else { // IPv6
            struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)rp->ai_addr;
            addr = &(ipv6->sin6_addr);
            ipver = "IPv6";
        }

        char ipstr[INET6_ADDRSTRLEN];
        inet_ntop(rp->ai_family, addr, ipstr, sizeof(ipstr));
        printf(" %s: %s\n", ipver, ipstr);
    }

    // 释放getaddrinfo分配的内存
    freeaddrinfo(result);
}
bool get_host_ip_and_port(const char* szHTTPHeader,char* pHostIP,int* pHostPort)
{
    if( (szHTTPHeader == NULL)||(strlen(szHTTPHeader)<strlen("Host: ")) )
        return false;
    char* szHostValue = parse_host_header(szHTTPHeader);
    if( (szHostValue == NULL) || (strlen(szHostValue)<strlen("Host: ")) )
        return false;
    char szHost[1024] = {'\0'};
    char* szPort = strstr(szHostValue, ":");
    if (szPort != NULL)
    {
        int iPort = atoi(szPort+1);
        if( (iPort>0) && (iPort<65535))
            *pHostPort = iPort;
        else
            return false;
        strncpy(szHost, szHostValue, szPort-szHostValue);
    }
    else
        strcpy(szHost, szHostValue);
    
    resolve_and_print_ip(szHost, pHostIP);
    if (strlen(pHostIP)<=7)
        return false;
    return true;
}
typedef struct thread_param{
    int isockfd;
    int iclient_addr_len;
    struct sockaddr_in * addr_client;
}thread_param;
bool get_dest_server_ip_and_port(const char* szHTTPHeader,char* pdest_server_ip,int* pdest_server_port)
{
    return get_host_ip_and_port(szHTTPHeader, pdest_server_ip, pdest_server_port);
}
void* sub_thread_handle_connection(void* arg)
{
    pthread_t tid_cur = pthread_self();
    printf("sub thread:%d start\n",tid_cur);

    thread_param *param = (thread_param *)arg;
    int iclient_socket = param->isockfd;
    struct sockaddr_in *client_addr = param->addr_client;
    socklen_t iclient_addr_len = param->iclient_addr_len;
    getpeername(iclient_socket,(struct sockaddr *)client_addr,&iclient_addr_len);
    char szclient_ip[50] = {0};
    int iclient_port = ntohs(client_addr->sin_port);
    inet_ntop(AF_INET,&client_addr->sin_addr,szclient_ip,sizeof(szclient_ip));
    printf("sub thread:%d new client->%s:%d  sock:%d  \n",tid_cur,szclient_ip,iclient_port,iclient_socket);
    char buffer[1024] = {0};
    ssize_t bytes_received = 0;
    // 读取客户端发送的数据
    int i = 0;
    while (1)
    {
        char tmprecvBuffer[1024] = {0};
        int tmpbytes_received = recv(iclient_socket, tmprecvBuffer, sizeof(tmprecvBuffer), 0);
        if (tmpbytes_received <= 0)
        {
            
            printf("client %s:%d disconnect on thread:%d with socketfd:%d \n",szclient_ip,iclient_port,tid_cur,iclient_socket);
            break;
        }
        else
        {
             // 处理收到的数据
            printf("sub thread:%lldd receive client->%s:%d  sock:%d  msg:\n%s\n",(long long)tid_cur,szclient_ip,iclient_port,iclient_socket,buffer);
            memcpy(buffer+bytes_received, tmprecvBuffer, tmpbytes_received);
            bytes_received+=tmpbytes_received;
            if (strstr(tmprecvBuffer, "\r\n\r\n")) // 只处理GET请求
            {
                break;
            }
        }
    }

    char method[100] = {0};
    char url[100] = {0};
    char version[30] = {0};
    sscanf(buffer, "%s %s %s",method,url,version);
    if (strcasecmp(method, "GET")!=0)
    {
        char* respose = "HTTP/1.0 501 Not Implemented\r\n";
        send(iclient_socket, respose, strlen(respose), 0);
        close(iclient_socket);
        return NULL;
    }
    char dest_server_ip[] = {"172.18.100.130"};
    int dest_server_port = 80;
    
    if (!get_dest_server_ip_and_port(buffer,dest_server_ip,&dest_server_port))
    {
        char* respose = "HTTP/1.0 501 can not resolve host\r\n";
        send(iclient_socket, respose, strlen(respose), 0);
        close(iclient_socket);
        return NULL;
    }
    
    char receivBuff[1024] = {0};
    int proxyClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    struct sockaddr_in destAddr;
    destAddr.sin_addr.s_addr = inet_addr(dest_server_ip);
    destAddr.sin_port = htons(dest_server_port);
    destAddr.sin_family = AF_INET;
    int iconnect_result = connect(proxyClientSocket, (struct sockaddr *)&destAddr, sizeof(destAddr));
    if (iconnect_result == -1)
    {
        printf("HTTP proxy server connect to dest server %s:%d ",dest_server_ip,dest_server_port);
        perror("");
        close(proxyClientSocket);
        return NULL;
    }
    printf("HTTP proxy server connect to dest server %s:%d ok then forward request from client to dest server \n",dest_server_ip,dest_server_port);
    int isend_result = send(proxyClientSocket, buffer, strlen(buffer), 0);
    if (isend_result>0)
    {
        printf("HTTP proxy server forward data from client to dest server %s:%d ok %d\n",dest_server_ip,dest_server_port,isend_result);
    }
    else
        printf("HTTP proxy server forward data from client to dest server %s:%d \n",dest_server_ip,dest_server_port);
    
    int receivedLen = 0;
    while (1)
    {
        receivedLen = recv(proxyClientSocket, receivBuff, 1024, 0);
        if (receivedLen<=0)
        {
            printf("HTTP proxy server recv data from destserver %s:%d \n",dest_server_ip,dest_server_port);
            break;
        }
        else
        {
            printf("HTTP proxy server receive %d bytes data from dest server %s:%d\n",receivedLen,dest_server_ip,dest_server_port);
            int isend_result = send(iclient_socket, receivBuff, receivedLen, 0);
            if (isend_result<=0)
            {
                printf("HTTP proxy server forward data from destserver to client fail\n");
                break;
            }
            else
            {
                printf("HTTP proxy server forwarded response %d data from dest server %s:%d to client %s\n\n",isend_result,dest_server_ip,dest_server_port,szclient_ip);
            }
            memset(receivBuff, 0, 1024);
        }
    }
   
    printf("sub thread:%lld stop client->%s:%d  sock:%d  \n",(long long)tid_cur,szclient_ip,iclient_port,iclient_socket);
    close(proxyClientSocket);
    close(iclient_socket);
    return NULL;
}

bool get_config_from_param(int argc,const char* argv[],int* iserver_addr,int* iserver_port)
{
    if (argc<3)
    {
        printf("param usage \n  A 8000 :accept all client include remote at port 8000\n  L 8000 :only accept local client inside this computer at port 8000 \n");
        return false;
    }
    if (strcmp(argv[1], "A")==0)
    {
        *iserver_addr = INADDR_ANY;
    }
    else if (strcmp(argv[1],"L")==0)
    {
        *iserver_addr = inet_addr("127.0.0.1");;
    }
    else
    {
        printf("param usage \n  A 8000 :accept all client include remote at port 8000\n  L 8000 :only accept local client inside this computer at port 8000 \n");
        return false;
    }
    
    if( (atoi(argv[2]) > 1024) && (atoi(argv[2]) < 65535))
    {
        *iserver_port = atoi(argv[2]);
    }
    else
    {
        printf("param usage \n  A 8000 :accept all client include remote at port 8000\n  L 8000 :only accept local client inside this computer at port 8000\n");
        return false;
    }
    return true;
}
int main(int argc, const char * argv[]) {
    // insert code here...
    int MAX_ACCEPT_CLIENT_CNT = 1000;
    int iserver_port = 8888;
    int iserver_addr = INADDR_ANY;
   if (!get_config_from_param(argc,argv,&iserver_addr,&iserver_port))
       return 0;
    
    struct sockaddr_in proxy_server_addr;
    bzero(&proxy_server_addr, sizeof(proxy_server_addr));
    proxy_server_addr.sin_family = AF_INET;
    proxy_server_addr.sin_port = htons(iserver_port);
    proxy_server_addr.sin_addr.s_addr = iserver_addr;//INADDR_ANY;//INADDR_LOOPBACK;//inet_addr("127.0.0.1");
    
    int iproxy_server_socket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if (iproxy_server_socket<=0)
    {
        perror("create proxy server socket fail ");
        return 0;
    }
    
    int ibind_result = bind(iproxy_server_socket, (struct sockaddr*)&proxy_server_addr, sizeof(struct sockaddr));
    if (ibind_result!=0)
    {
        perror("bind proxy server socket fail ");
        close(iproxy_server_socket);
        return 0;
    }
    
    char szserver_ip[20]={0};
    const char* szserver_ip_tmp = inet_ntop(AF_INET,&proxy_server_addr.sin_addr,szserver_ip,sizeof(szserver_ip));
    if (szserver_ip_tmp == NULL)
    {
        perror("get server ip fail ");
        close(iproxy_server_socket);
        return 0;
    }

    int ilisten_result = listen(iproxy_server_socket, MAX_ACCEPT_CLIENT_CNT);
    if (ilisten_result!=0)
    {
        printf("proxy server listen at %s:%d fail \n",szserver_ip,iserver_port);
        perror("");
        close(iproxy_server_socket);
        return 0;
    }
    printf(" HTTP proxy started listening at %s:%d\n",szserver_ip,iserver_port);
    
    while (1) {
        struct sockaddr_in client_addr;
        int client_sock_len;
        int client_sock = accept(iproxy_server_socket, &client_addr, &client_sock_len);
        if (client_sock<=0)
            continue;
        thread_param param;
        param.isockfd = client_sock;
        param.addr_client =&client_addr;
        param.iclient_addr_len = client_sock_len;
  
        getpeername(client_sock,(struct sockaddr *)&client_addr,&client_sock_len);
        char szclient_ip[50]={0};
        int iclient_port = ntohs(client_addr.sin_port);
        inet_ntop(AF_INET,&client_addr.sin_addr,szclient_ip,sizeof(szclient_ip));
        
        pthread_t tid;
        pthread_create(&tid, NULL, sub_thread_handle_connection, &param);
        printf("HTTP proxy server main thread accept new client from->%s:%d,and create new thread %d\n\n\n",szclient_ip,iclient_port,tid);
        pthread_detach(tid); // 线程分离，结束时自动回收资源
    }
    return 0;
}
